/*=========================================================================

  Library:   CFS

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  =========================================================================*/

// Qt includes
#include <QDebug>
#include <QMetaType>
#include <QObject>
#include <QState>

// CFS includes
#include "CFSWorkflowStep.h"
#include "CFSWorkflowStep_p.h"
#include "CFSWorkflow.h"

// STD includes
#include <iostream>

// --------------------------------------------------------------------------
// CFSWorkflowStepPrivate methods

// --------------------------------------------------------------------------
CFSWorkflowStepPrivate::CFSWorkflowStepPrivate(CFSWorkflowStep& object)
  :q_ptr(&object)
{
  qRegisterMetaType<CFSWorkflowStep*>("CFSWorkflowStep*");
  this->Workflow = 0;

  this->WidgetType = false;

  this->HasValidateCommand = false;
  this->HasOnEntryCommand = false;
  this->HasOnExitCommand = false;

  // Create state
  this->ProcessingState = new QState();
  this->ValidationState = new QState();

  // Create 'validation' transition
  this->ValidationTransition =
      new CFSWorkflowIntrastepTransition(CFSWorkflowIntrastepTransition::ValidationTransition);
  this->ValidationTransition->setTargetState(this->ValidationState);
  this->ProcessingState->addTransition(this->ValidationTransition);

  // Create 'failed validation' transation
  this->ValidationFailedTransition = 0;
  this->ValidationFailedTransition =
      new CFSWorkflowIntrastepTransition(CFSWorkflowIntrastepTransition::ValidationFailedTransition);
  this->ValidationFailedTransition->setTargetState(this->ProcessingState);
  this->ValidationState->addTransition(this->ValidationFailedTransition);
}

// --------------------------------------------------------------------------
CFSWorkflowStepPrivate::~CFSWorkflowStepPrivate()
{
  if (!this->ValidationState.isNull())
    {
    delete this->ValidationState;
    }
  if (!this->ProcessingState.isNull())
    {
    delete this->ProcessingState;
    }

  // If we delete the states, then Qt will handle deleting the transitions
}

// --------------------------------------------------------------------------
void CFSWorkflowStepPrivate::validationCompleteInternal(bool validationResults, const QString& branchId)const
{
  emit validationComplete(validationResults, branchId);
}

// --------------------------------------------------------------------------
void CFSWorkflowStepPrivate::onEntryCompleteInternal()const
{
  emit onEntryComplete();
}

// --------------------------------------------------------------------------
void CFSWorkflowStepPrivate::onExitCompleteInternal()const
{
  emit onExitComplete();
}

// --------------------------------------------------------------------------
void CFSWorkflowStepPrivate::invokeValidateCommandInternal(const QString& desiredBranchId)const
{
  emit invokeValidateCommand(desiredBranchId);
}

// --------------------------------------------------------------------------
void CFSWorkflowStepPrivate::invokeOnEntryCommandInternal(const CFSWorkflowStep* comingFrom, const CFSWorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  emit invokeOnEntryCommand(comingFrom, transitionType);
}

// --------------------------------------------------------------------------
void CFSWorkflowStepPrivate::invokeOnExitCommandInternal(const CFSWorkflowStep* goingTo, const CFSWorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  emit invokeOnExitCommand(goingTo, transitionType);
}

// --------------------------------------------------------------------------
// CFSWorkflowStep methods

// --------------------------------------------------------------------------
CFSWorkflowStep::CFSWorkflowStep(): d_ptr(new CFSWorkflowStepPrivate(*this))
{
}

// --------------------------------------------------------------------------
CFSWorkflowStep::CFSWorkflowStep(const QString& newId)
  : d_ptr(new CFSWorkflowStepPrivate(*this))
{
  Q_D(CFSWorkflowStep);
  d->Id = newId;
}

// --------------------------------------------------------------------------
CFSWorkflowStep::CFSWorkflowStep(CFSWorkflowStepPrivate * pimpl,
                                 const QString& newId):d_ptr(pimpl)
{
  Q_D(CFSWorkflowStep);
  d->Id = newId;
}

// --------------------------------------------------------------------------
CFSWorkflowStep::~CFSWorkflowStep()
{
}

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, CFSWorkflow*, workflow, Workflow);
CFS_SET_CPP(CFSWorkflowStep, CFSWorkflow*, setWorkflow, Workflow);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, QString, id, Id);

// --------------------------------------------------------------------------
void CFSWorkflowStep::setId(const QString& newId)
{
  Q_D(CFSWorkflowStep);
  if (d->Workflow && d->Workflow->hasStep(newId) && !this->id().isEmpty())
    {
    qWarning() << QString("CFSWorkflowStep - Failed to change id from '%1' to '%2' - "
                          "Step already added to a workflow !").arg(this->id()).arg(newId);
    return;
    }
  d->Id = newId;
}

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, QString, name, Name);
CFS_SET_CPP(CFSWorkflowStep, const QString&, setName, Name);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, QString, description, Description);
CFS_SET_CPP(CFSWorkflowStep, const QString&, setDescription, Description);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, QString, statusText, StatusText);
CFS_SET_CPP(CFSWorkflowStep, const QString&, setStatusText, StatusText);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, bool, hasValidateCommand, HasValidateCommand);
CFS_SET_CPP(CFSWorkflowStep, bool, setHasValidateCommand, HasValidateCommand);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, bool, hasOnEntryCommand, HasOnEntryCommand);
CFS_SET_CPP(CFSWorkflowStep, bool, setHasOnEntryCommand, HasOnEntryCommand);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, bool, hasOnExitCommand, HasOnExitCommand);
CFS_SET_CPP(CFSWorkflowStep, bool, setHasOnExitCommand, HasOnExitCommand);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, QState*, processingState, ProcessingState);
CFS_GET_CPP(CFSWorkflowStep, QState*, validationState, ValidationState);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, CFSWorkflowIntrastepTransition*, validationTransition, ValidationTransition);
CFS_GET_CPP(CFSWorkflowStep, CFSWorkflowIntrastepTransition*,
            validationFailedTransition, ValidationFailedTransition);

// --------------------------------------------------------------------------
CFS_GET_CPP(CFSWorkflowStep, bool, isWidgetType, WidgetType);

// --------------------------------------------------------------------------
QObject* CFSWorkflowStep::CFSWorkflowStepQObject()
{
  Q_D(CFSWorkflowStep);
  return d;
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::validationComplete(bool validationResults, const QString& branchId)const
{
  Q_D(const CFSWorkflowStep);
  d->validationCompleteInternal(validationResults, branchId);
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::onEntryComplete()const
{
  Q_D(const CFSWorkflowStep);
  d->onEntryCompleteInternal();
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::onExitComplete()const
{
  Q_D(const CFSWorkflowStep);
  d->onExitCompleteInternal();
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::invokeValidateCommand(const QString& desiredBranchId)const
{
  Q_D(const CFSWorkflowStep);
  d->invokeValidateCommandInternal(desiredBranchId);
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::invokeOnEntryCommand(const CFSWorkflowStep* comingFrom, const CFSWorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  Q_D(const CFSWorkflowStep);
  d->invokeOnEntryCommandInternal(comingFrom, transitionType);
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::invokeOnExitCommand(const CFSWorkflowStep* goingTo, const CFSWorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  Q_D(const CFSWorkflowStep);
  d->invokeOnExitCommandInternal(goingTo, transitionType);
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::validate(const QString& desiredBranchId)
{
  Q_D(CFSWorkflowStep);
  if (this->workflow()->verbose())
    {
    qDebug() << QString("validate - validating the input from %1").arg(d->Name);
    }

  this->validationComplete(true, desiredBranchId);
}


// --------------------------------------------------------------------------
void CFSWorkflowStep::onEntry(const CFSWorkflowStep* comingFrom,
                              const CFSWorkflowInterstepTransition::InterstepTransitionType transitionType)
{
  Q_UNUSED(comingFrom);
  Q_UNUSED(transitionType);

  // Signals that we are finished
  this->onEntryComplete();
}

// --------------------------------------------------------------------------
void CFSWorkflowStep::onExit(const CFSWorkflowStep* goingTo,
                             const CFSWorkflowInterstepTransition::InterstepTransitionType transitionType)
{
  Q_UNUSED(goingTo);
  Q_UNUSED(transitionType);

 // Signals that we are finished
 this->onExitComplete();
}
